package options

import (
	"flag"
	"fmt"
	"sync"
	"time"

	"gitee.com/youkelike/ziam/zsdk2/rest"
	"gitee.com/youkelike/zlog"
	"github.com/AlekSi/pointer"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
)

const (
	FlagZctlConfig  = "zctlconfig"
	FlagHost        = "host"
	FlagUsername    = "username"
	FlagPassword    = "password"
	FlagBearerToken = "bearerToken"
	FlagSecretID    = "secretId"
	FlagSecretKey   = "secretKey"

	FlagCertFile   = "tlsClient.certFile"
	FlagKeyFile    = "tlsClient.keyFile"
	FlagServerName = "tlsClient.serverName"
	FlagInsecure   = "tlsClient.insecure"
	FlagCAFile     = "tlsClient.caFile"
	FlagSecureHost = "tlsClient.secureHost"

	FlagTimeout       = "timeout"
	FlagMaxRetries    = "maxRetries"
	FlagRetryInterval = "retryInterval"
)

type RESTClientGetter interface {
	ToRESTConfig() (*rest.Config, error)
	// ToRawZiamConfigLoader() clientcmd.ClientConfig
}

var _ RESTClientGetter = &ConfigFlags{}

type ConfigFlags struct {
	ZctlConfig *string

	Host        *string
	BearerToken *string
	Username    *string
	Password    *string
	SecretID    *string
	SecretKey   *string

	Insecure   *bool
	SecureHost *string
	ServerName *string
	CertFile   *string
	KeyFile    *string
	CAFile     *string

	Timeout       *time.Duration
	MaxRetries    *int
	RetryInterval *time.Duration

	restConfig *rest.Config
	lock       sync.Mutex
	// If set to true, will use persistent client config and
	// propagate the config to the places that need it, rather than
	// loading the config multiple times
	usePersistentConfig bool
}

func (f *ConfigFlags) ToRESTConfig() (*rest.Config, error) {
	return f.ToRawZiamConfigLoader(), nil
}

func (f *ConfigFlags) ToRawZiamConfigLoader() *rest.Config {
	if f.usePersistentConfig {
		return f.toRawZiamPersistentConfigLoader()
	}
	return f.toRawZiamConfigLoader()
}

// 每次都重新用配置文件中的值生成一份新的配置
func (f *ConfigFlags) toRawZiamConfigLoader() *rest.Config {
	config := &rest.Config{}
	if err := viper.Unmarshal(&config); err != nil {
		panic(err)
	}
	zlog.Errorw("===", "viper", viper.AllSettings())
	zlog.Errorw("...", "config", config)
	return config
}

// 把配置缓存后再返回
func (f *ConfigFlags) toRawZiamPersistentConfigLoader() *rest.Config {
	f.lock.Lock()
	defer f.lock.Unlock()

	if f.restConfig == nil {
		f.restConfig = f.toRawZiamConfigLoader()
	}
	return f.restConfig
}

func (f *ConfigFlags) AddFlags(flags *pflag.FlagSet) {
	if f.ZctlConfig != nil {
		flags.StringVar(f.ZctlConfig, FlagZctlConfig, *f.ZctlConfig,
			fmt.Sprintf("Path to the %s file to use for CLI requests", FlagZctlConfig))
	}

	if f.BearerToken != nil {
		flags.StringVar(
			f.BearerToken,
			FlagBearerToken,
			*f.BearerToken,
			"Bearer token for authentication to the API server",
		)
	}

	if f.Username != nil {
		flags.StringVar(f.Username, FlagUsername, *f.Username, "Username for basic authentication to the API server")
	}

	if f.Password != nil {
		flags.StringVar(f.Password, FlagPassword, *f.Password, "Password for basic authentication to the API server")
	}

	if f.SecretID != nil {
		flags.StringVar(f.SecretID, FlagSecretID, *f.SecretID, "SecretID for JWT authentication to the API server")
	}

	if f.SecretKey != nil {
		flags.StringVar(f.SecretKey, FlagSecretKey, *f.SecretKey, "SecretKey for jwt authentication to the API server")
	}

	if f.CertFile != nil {
		flags.StringVar(f.CertFile, FlagCertFile, *f.CertFile, "Path to a client certificate file for TLS")
	}
	if f.KeyFile != nil {
		flags.StringVar(f.KeyFile, FlagKeyFile, *f.KeyFile, "Path to a client key file for TLS")
	}
	if f.ServerName != nil {
		flags.StringVar(f.ServerName, FlagServerName, *f.ServerName, ""+
			"Server name to use for server certificate validation. If it is not provided, the hostname used to contact the server is used")
	}
	if f.Insecure != nil {
		flags.BoolVar(f.Insecure, FlagInsecure, *f.Insecure, ""+
			"If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure")
	}
	if f.CAFile != nil {
		flags.StringVar(f.CAFile, FlagCAFile, *f.CAFile, "Path to a cert file for the certificate authority")
	}

	if f.Host != nil {
		flags.StringVarP(f.Host, FlagHost, "s", *f.Host, "The address and port of the IAM API server")
	}

	if f.Timeout != nil {
		flags.DurationVar(
			f.Timeout,
			FlagTimeout,
			*f.Timeout,
			"The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests.",
		)
	}

	if f.MaxRetries != nil {
		flag.IntVar(f.MaxRetries, FlagMaxRetries, *f.MaxRetries, "Maximum number of retries.")
	}

	if f.RetryInterval != nil {
		flags.DurationVar(
			f.RetryInterval,
			FlagRetryInterval,
			*f.RetryInterval,
			"The interval time between each attempt.",
		)
	}
}

func (f *ConfigFlags) WithDeprecatedPasswordFlag() *ConfigFlags {
	f.Username = pointer.ToString("")
	f.Password = pointer.ToString("")

	return f
}

func (f *ConfigFlags) WithDeprecatedSecretFlag() *ConfigFlags {
	f.SecretID = pointer.ToString("")
	f.SecretKey = pointer.ToString("")

	return f
}

func NewConfigFlags(usePersistentConfig bool) *ConfigFlags {
	// 在这里主动设置了值的字段，才会被添加到命令行选项中,
	// 用指针类型，方便判断是否设置了值
	return &ConfigFlags{
		ZctlConfig: pointer.ToString(""),

		BearerToken: pointer.ToString(""),
		Insecure:    pointer.ToBool(false),
		ServerName:  pointer.ToString(""),
		CertFile:    pointer.ToString(""),
		KeyFile:     pointer.ToString(""),
		CAFile:      pointer.ToString(""),

		Host:                pointer.ToString(""),
		Timeout:             pointer.ToDuration(30 * time.Second),
		MaxRetries:          pointer.ToInt(0),
		RetryInterval:       pointer.ToDuration(1 * time.Second),
		usePersistentConfig: usePersistentConfig,
	}
}
